#include "StdAfx.h"
#include "MeshProject.h"

namespace Lit
{
	namespace Editor
	{
		void ObjMeshFormat::processComment( const std::string &str, size_t line_id){
			if( !str.empty() )
			LIT_LOG_DBG( "Found comment '"+str+"' at line "+String(line_id).str() );
		}
		void ObjMeshFormat::processVertex( const std::string &str, size_t line_id, ObjMeshFormat::VertexList& lst ){
			LIT_LOG_DBG( "Found vertex '"+str+"' at line "+String(line_id).str() );
			lst.push_back( Ogre::StringConverter::parseVector3(str) );
		}
		void ObjMeshFormat::processNormal( const std::string &str, size_t line_id, ObjMeshFormat::NormalList& lst ){
			LIT_LOG_DBG( "Found normal '"+str+"' at line "+String(line_id).str() );
			lst.push_back( Ogre::StringConverter::parseVector3(str) );
		}
		void ObjMeshFormat::processTextureCoord( const std::string &str, size_t line_id, ObjMeshFormat::TexCoordList& lst ){
			LIT_LOG_DBG( "Found texture coord '"+str+"' at line "+String(line_id).str() );
			lst.push_back( Ogre::StringConverter::parseVector2(str) );
		}
		ObjMeshFormat::VertexInfos ObjMeshFormat::processFaceInfos( const std::string &str, size_t line_id ){
			Ogre::StringVector toks(Ogre::StringUtil::tokenise( str, "/" ));
			if( toks.size() != 3 ) 
				throw Lit::InvalidParameter("Face Infos", "Face Infos "+str+" at line "+String(line_id).str()+" in incorrectly formed !", EXC_LOC );
			return VertexInfos( Ogre::StringConverter::parseUnsignedInt( toks[0] )-1, Ogre::StringConverter::parseUnsignedInt( toks[2] )-1, Ogre::StringConverter::parseUnsignedInt( toks[1] )-1 );
		}
		void ObjMeshFormat::processFace( const std::string &str, size_t line_id, ObjMeshFormat::FaceMap& lst, const std::string &grp ){
			LIT_LOG_DBG( "Found face '"+str+"' at line "+String(line_id).str() );
			if( lst.find(grp) == lst.end() ) lst[grp] = FaceList();
			Ogre::StringVector toks(Ogre::StringUtil::tokenise( str, " " ));
			if( toks.size() != 3 && toks.size() != 4 ) throw Lit::InvalidParameter("Face", "Face "+str+" at line "+String(line_id).str()+" in incorrectly formed, not a quad nor a triangle !", EXC_LOC );
			if( toks.size() == 3 )
			{
				lst[grp].numTris ++;
				lst[grp].list.push_back(FaceInfos(processFaceInfos( toks[0], line_id ), processFaceInfos( toks[1], line_id ), processFaceInfos( toks[2], line_id )));
			}
			else if( toks.size() == 4 )
			{
				lst[grp].list.push_back(FaceInfos(processFaceInfos( toks[0], line_id ), processFaceInfos( toks[1], line_id ), processFaceInfos( toks[2], line_id ), processFaceInfos( toks[3], line_id )));
				lst[grp].numQuads ++;
			}
		}
		void ObjMeshFormat::processObject( const std::string &str, size_t line_id, std::string & grp ){
			LIT_LOG_DBG( "Found object '"+str+"' at line "+String(line_id).str() );
		}
		void ObjMeshFormat::processGroup( const std::string &str, size_t line_id, std::string & grp ){
			LIT_LOG_DBG( "Found group '"+str+"' at line "+String(line_id).str() );
			grp = str;
		}
		
		void ObjMeshFormat::processUseMtl( const std::string &str, size_t line_id, std::string & material )
		{
			LIT_LOG_DBG("Switching to material '"+str+"'");
			material = str;
		}
		void ObjMeshFormat::processMaterialLibLine( const std::string &word, const std::string &rest, size_t line_id, Ogre::MaterialPtr mat ){
			
		}
		void ObjMeshFormat::processMaterialLib( const std::string &str, size_t line_id, FaceList& lst )
		{
			FileLineParser pars( str, false );
			if( !pars.isOpen() ) { LIT_LOG_DBG("Cannot find material library '"+str+"'"); return; }
			LIT_LOG_DBG("Parsing material library '"+str+"'");
			while(!pars.eof())
			{
				FileLineParser::Line l;
				pars >> l;
				std::string word;
				std::stringstream ss(l.line);
				ss >> word;
				MeshConverter::Format::trim( word );
				std::string rest(MeshConverter::Format::trim(ss.str()));
				if(word == "newmtl"){ 
					rest = MeshConverter::Format::eatLeft( rest, 6 );  
					LIT_LOG_DBG("Found material "+rest);
					Ogre::MaterialManager::getSingleton().create( rest, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME );
					lst.material = rest;
				}
				else if( word == "Ka" )
				{
					rest = MeshConverter::Format::eatLeft( rest, 2 );  
					Ogre::MaterialPtr mat(Ogre::MaterialManager::getSingleton().getByName(lst.material));
					if( lst.material != "BaseWhite" )
						mat->setAmbient( Ogre::StringConverter::parseColourValue( rest ) );
					LIT_LOG_DBG("Found ambient: "+rest);
				}
				else if( word == "Ks" )
				{
					rest = MeshConverter::Format::eatLeft( rest, 2 ); 
					Ogre::MaterialPtr mat(Ogre::MaterialManager::getSingleton().getByName(lst.material));
					if( lst.material != "BaseWhite" )
						mat->setSpecular( Ogre::StringConverter::parseColourValue( rest ) ); 
					LIT_LOG_DBG("Found specular: "+rest);
				}
				else if( word == "Kd" )
				{
					rest = MeshConverter::Format::eatLeft( rest, 2 );  
					Ogre::MaterialPtr mat(Ogre::MaterialManager::getSingleton().getByName(lst.material));
					if( lst.material != "BaseWhite" )
						mat->setDiffuse( Ogre::StringConverter::parseColourValue( rest ) );
					LIT_LOG_DBG("Found diffuse: "+rest);
				}
				else if( word == "Ns" )
				{
					rest = MeshConverter::Format::eatLeft( rest, 2 );  
					Ogre::MaterialPtr mat(Ogre::MaterialManager::getSingleton().getByName(lst.material));
					if( lst.material != "BaseWhite" )
						mat->setShininess( Ogre::StringConverter::parseReal( rest ) );
					LIT_LOG_DBG("Found shininess: "+rest);
				}
			}
			pars.close();
		}
		MeshConverterObjFormat::MeshConverterObjFormat( ) : Format( "WaveFront Obj Format", "obj", true, true ){}
		MeshConverterObjFormat::~MeshConverterObjFormat(){}

		Ogre::MeshPtr MeshConverterObjFormat::importMesh( const std::string &input_filename, const std::string &ogre_mesh_name )
		{
			if( Ogre::MeshManager::getSingleton().resourceExists( ogre_mesh_name ) )
				throw ItemDuplicate( ogre_mesh_name, "Ogre::MeshManager", EXC_LOC );
			FileLineParser parser;
			if( !parser.open( input_filename, false ) )
				throw FileMissing( input_filename, EXC_LOC );
			mVertices.clear();
			mNormals.clear();
			mTexCoords.clear();
			mFaces.clear();
			mGroup = "__default__";
			FileLineParser::Line line;
			while( !parser.eof() )
			{
				parser >> line;
				Ogre::StringUtil::trim(line.line);
				if( !line.line.empty() )
				{
					std::string word;
					std::stringstream ss(line.line);
					ss >> word;
					Ogre::StringUtil::trim( word );
					std::string rest = ss.str();
					if(word == "#"){ rest = eatLeft( rest, 1 ); ObjMeshFormat::processComment( rest, line.id ); }
					else if(word == "v"){ rest = eatLeft( rest, 1 ); ObjMeshFormat::processVertex( rest, line.id, mVertices ); }
					else if(word == "vn"){ rest = eatLeft( rest, 2 ); ObjMeshFormat::processNormal( rest, line.id, mNormals ); }
					else if(word == "vt"){ rest = eatLeft( rest, 2 ); ObjMeshFormat::processTextureCoord( rest, line.id, mTexCoords ); }
					else if(word == "f"){ rest = eatLeft( rest, 1 ); ObjMeshFormat::processFace( rest, line.id, mFaces, mGroup ); }
					else if(word == "o"){ rest = eatLeft( rest, 1 ); ObjMeshFormat::processObject( rest, line.id, mGroup ); }
					else if(word == "g"){ rest = eatLeft( rest, 1 ); ObjMeshFormat::processGroup( rest, line.id, mGroup ); }
					else if(word == "mtllib" ){ rest = eatLeft( rest, 6 ); ObjMeshFormat::processMaterialLib( FilePath(input_filename).dirName()+"/"+rest, line.id, mFaces[mGroup] ); }
					else if(word == "usemtl" ){ rest = eatLeft( rest, 6 ); ObjMeshFormat::processUseMtl( rest, line.id, mFaces[mGroup].material ); }
				}
			}
			parser.close();
			return createMesh( ogre_mesh_name );
		}
		Ogre::MeshPtr MeshConverterObjFormat::createMesh( const std::string &mesh_name )
		{ 
			using namespace Ogre;
			Ogre::String name = mesh_name;

			MeshPtr mesh = MeshManager::getSingleton().createManual(name,Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
			
			if( mVertices.empty() ){ return mesh; }
			AxisAlignedBox aabox;
			Lit::Vec3 * verts = (!mVertices.empty())?&mVertices[0]:NULL;
			Lit::Vec3 * normals = (!mNormals.empty())?&mNormals[0]:NULL;
			for( auto i=mFaces.begin(); i!=mFaces.end(); i++ )
			{
				if( !i->second.list.empty() )
				{
					ObjMeshFormat::FaceInfos * faces = (i->second.list.empty()?NULL:(&i->second.list[0]));
					int nVtx = mVertices.size();
					int nFace = i->second.list.size();
 
					//Create OgreMesh
					try
					{
						SubMesh *submesh = mesh->createSubMesh(i->first);
						//printf("Building manual OGRE mesh\n");
 
						submesh->useSharedVertices = false;
						submesh->vertexData = new VertexData();
						submesh->vertexData->vertexStart = 0;
						submesh->vertexData->vertexCount = nVtx + 1;
						VertexDeclaration * dec = submesh->vertexData->vertexDeclaration;
						static const unsigned short source = 0;
						static const unsigned short csource = 1;
						size_t offset = 0;
 
						offset += dec->addElement(source,offset,VET_FLOAT3,VES_POSITION).getSize();
						offset += dec->addElement(source,offset,VET_FLOAT3,VES_NORMAL).getSize();
 
						//printf("  - Assigning Vertex Buffer: ");
						HardwareVertexBufferSharedPtr vbuffer = HardwareBufferManager::getSingleton().createVertexBuffer(dec->getVertexSize(source),\
																															submesh->vertexData->vertexCount,\
																															HardwareBuffer::HBU_STATIC_WRITE_ONLY);
 
						//printf("1...");
						float *vdata = static_cast<float*>(vbuffer->lock(HardwareBuffer::HBL_DISCARD));
 
						ObjMeshFormat::NormalList::const_iterator mIter;
 
						for(int ii = 0; ii < nVtx; ii++){
							Vec3& v(mVertices[ii]);
							Vec3 p(v), n(findNormal(&i->second, v));
							*vdata++ = p.x; *vdata++ = p.y; *vdata++ = p.z;
 							*vdata++ = n.x; *vdata++ = n.y; *vdata++ = n.z;
							aabox.merge(p);
						}
 
						vbuffer->unlock();
 
						//printf("Complete.\n");
 
						//printf("  - Assigning Face buffer");
						submesh->vertexData->vertexBufferBinding->setBinding(source,vbuffer);
						submesh->indexData->indexStart = 0;
						submesh->indexData->indexCount = i->second.numQuads * 6 + i->second.numTris * 3;
						submesh->indexData->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer(HardwareIndexBuffer::IT_32BIT,submesh->indexData->indexCount,HardwareBuffer::HBU_STATIC_WRITE_ONLY);
 
						//printf("1...");
						uint32 *idata = static_cast<uint32*>(submesh->indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
 
						for(int ii = 0; ii < nFace; ii++){
							if( faces[ii].is_quad )
							{
								*idata++ = faces[ii].v0.v;
								*idata++ = faces[ii].v1.v;
								*idata++ = faces[ii].v2.v;

								*idata++ = faces[ii].v2.v;
								*idata++ = faces[ii].v3.v;
								*idata++ = faces[ii].v0.v;
							}
							else
							{
								*idata++ = faces[ii].v0.v;
								*idata++ = faces[ii].v1.v;
								*idata++ = faces[ii].v2.v;
							}
						}
 
						submesh->indexData->indexBuffer->unlock();
					
						submesh->setMaterialName( i->second.material );
						//printf("Complete.\n");
					}
					catch (std::exception& e){
						LIT_LOG(e.what());//printf("Problem creating ogre mesh: %s\n",e.getDescription().c_str());
						throw;
					}
				}
			}
			//printf("  - Create mesh boundaries...");
			mesh->_setBounds(aabox);
			mesh->_setBoundingSphereRadius( (aabox.getMaximum()-aabox.getMinimum()).length() / 2.0);
			//printf("Complete.\n");
 
					//Create a customcolour in your own material scripts.  Mine sets ambient and diffuse
					//colors to use whatever was entered manually.
			//submesh->setMaterialName("CustomColour");
			//printf("  - Loading finalized mesh...");
			mesh->load();
			return mesh;
		}
		void MeshConverterObjFormat::exportMesh( const std::string &output_filename, Ogre::MeshPtr mesh ){
			throw Lit::ImplementationMissing("MeshConverterObjFormat::exportMesh", EXC_LOC);
		}
		MeshConverterOGREFormat::MeshConverterOGREFormat( ) : Format( "Ogre Mesh Format v1.4", "mesh", true, true ){ }
		MeshConverterOGREFormat::~MeshConverterOGREFormat( ){}

		Ogre::MeshPtr MeshConverterOGREFormat::importMesh( const std::string &input_filename, const std::string &ogre_mesh_name )
		{
			Ogre::MeshSerializer ser;
			FilePath p( input_filename );
			Ogre::ResourceGroupManager::getSingleton().addResourceLocation( p.dirName(), "FileSystem", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true );
			FILE * f =fopen( input_filename.c_str(), "rb+" );
			Ogre::DataStreamPtr stream( new Ogre::FileHandleDataStream( input_filename, f, Ogre::DataStream::READ ) );
			Ogre::Mesh * msh = NULL;
			ser.importMesh( stream, msh  );
			return Ogre::MeshPtr(msh);
		}
		void MeshConverterOGREFormat::exportMesh( const std::string &output_filename, Ogre::MeshPtr mesh )
		{
			bool expMat = (QMessageBox::question( qApp->activeWindow(), "Export Material", "Do you want to export the material file too ?", QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes);
			Ogre::MeshSerializer().exportMesh( mesh.get(), output_filename  );
			Ogre::MaterialPtr mat(Ogre::MaterialManager::getSingleton().getByName(mesh->getSubMesh(0)->getMaterialName()));
			std::string matname = output_filename;
			size_t p = matname.find_last_of('.');
			if( p != std::string::npos ) matname = matname.substr( 0, p );
			
			std::string fmatname = matname+".material";
			std::string pmatname = matname+".program";
			Ogre::MaterialSerializer().exportMaterial( mat, fmatname );

		}
		
		FilePath MeshProject::BasePath = Lit::FileSystem::applicationPath()+"/editor";
		FilePath MeshProject::ImportPath = MeshProject::BasePath.str()+"/import";
		FilePath MeshProject::ExportPath = MeshProject::BasePath.str()+"/export";
		FilePath MeshProject::UserProjectPath = MeshProject::BasePath.str()+"/UserProjects/";

		MeshProject::MeshProject( Instance * inst, QTreeWidget*w, const std::string &name )
			: Project( inst, w, name, ".mesh_project" )
			, mFacingDir( NULL )
			, mPhysicsDynamic( NULL )
			, mPhysicsShape( NULL )
			, mPhysicsMass( NULL )
			, mPhysicsOffset( NULL )
			, mMaterialName( NULL )
			, mGeometryFacing( NULL )
			, mInstance( inst )
		{
			mMeshConverter.addFormat( new MeshConverterOGREFormat() );
			mMeshConverter.addFormat( new MeshConverterObjFormat() );
		}
		MeshProject::~MeshProject( )
		{
		}
		
		void MeshProject::_destroyProject( )
		{
			if( mFacingDir )
			{
				delete mFacingDir, mFacingDir = NULL;
			}
			if( mBody )
			{
				Lit::Root::PhysicsManager()->getWorld()->destroyBody( mBody );
			}
			mFacingDir= NULL;
			mPhysicsDynamic= NULL;
			mPhysicsShape= NULL;
			mPhysicsMass= NULL;
			mPhysicsOffset= NULL;
			mMaterialName= NULL;
			mGeometryFacing= NULL;
			mBody = NULL;
			mEntity = NULL;
		}
		void MeshProject::createPack( )
		{
			MeshPackExporterConfig dlg(mInstance->getMainWindow());
			dlg.exec( this );
			if( dlg.nothingToExport() )
			{
				QMessageBox::warning(mInstance->getMainWindow(), "Cannot create mesh pack", "Nothing to export in mesh pack !" );
				return;
			}
		}
		void MeshProject::setupGeometryNode( QTreeWidgetItem * node )
		{
			mGeometryFacing = new QComboBox( mTree );
			mGeometryFacing->addItem( "UNIT_X" );
			mGeometryFacing->addItem( "UNIT_Y" );
			mGeometryFacing->addItem( "UNIT_Z" );
			mGeometryFacing->addItem( "NEGATIVE_UNIT_X" );
			mGeometryFacing->addItem( "NEGATIVE_UNIT_Y" );
			mGeometryFacing->addItem( "NEGATIVE_UNIT_Z" );

			setWidget( createNode( "Facing Direction", node ), mGeometryFacing );

			connect( mGeometryFacing, SIGNAL(currentIndexChanged( const QString & )), this, SLOT(geometryFacingDirChanged(const QString &)) );
		}
		void MeshProject::setupPhysicsNode( QTreeWidgetItem * node )
		{
			mPhysicsShape = new QComboBox( mTree );
				mPhysicsShape->addItem( "Null" );
				mPhysicsShape->addItem( "Box" );
				mPhysicsShape->addItem( "Plane" );
				mPhysicsShape->addItem( "Sphere" );
				mPhysicsShape->setCurrentIndex( mBody->getShape()->shapeType() );			
			setWidget( createNode( "Shape", node ), mPhysicsShape );

			mPhysicsDynamic = new QCheckBox(mTree);
			mPhysicsDynamic->setChecked( !mBody->isStatic() );
			setWidget( createNode( "Dynamic", node ), mPhysicsDynamic );

			mPhysicsMass = new QDoubleSpinBox( mTree );
			mPhysicsMass->setRange( 0.0, 1000.0 );
			mPhysicsMass->setValue( mBody->getMass() );
			setWidget( createNode( "Mass", node ), mPhysicsMass );

			mPhysicsOffset = new QLineEdit( TO_QT(Ogre::StringConverter::toString( mBody->getShape()->initialMatrix().getPosition() )), mTree );
			setWidget( createNode( "Offset", node ), mPhysicsOffset );
			
			connect( mPhysicsShape, SIGNAL(currentIndexChanged( const QString & )), this, SLOT(physicsShapeChanged(const QString &)) );
			connect( mPhysicsDynamic, SIGNAL(toggled( bool )), this, SLOT(physicsDynamicStateChanged(bool)) );
			connect( mPhysicsMass, SIGNAL(valueChanged(double)), this, SLOT(physicsMassChanged(double)) );
		}
		void MeshProject::setupMaterialNode( QTreeWidgetItem * node )
		{
			mMaterialName = new QComboBox( mTree );
			Lit::ResourcesListPtr list=Lit::Root::Instance().getResourcesList();
			size_t matId = 0;
			for( auto i=list->materials().begin(); i!=list->materials().end(); i++ )
			{
				mMaterialName->addItem( TO_QT(i->getName()) );
				if( i->getName() == mEntity->getMaterialName(0) )
					mMaterialName->setCurrentIndex( matId );
				matId ++;
			}
			setWidget( createNode( "Name", node ), mMaterialName );

			connect( mMaterialName, SIGNAL(currentIndexChanged( const QString & )), this, SLOT(materialChanged(const QString &)) );
		}
		void MeshProject::setupNodes( )
		{
			if( !mRoot ) return;
			QTreeWidgetItem * phyNode = createRootNode( "Physics" );
			QTreeWidgetItem * matNode = createRootNode( "Material" );
			QTreeWidgetItem * geoNode = createRootNode( "Geometry" );

			setupGeometryNode( geoNode );
			setupMaterialNode( matNode );
			setupPhysicsNode( phyNode );
		}
		

		void MeshProject::open(const std::string &filename)
		{
			std::string path = filename;
			if( filename.empty() )
				path = FileDialog::getOpenFileName( getInstance(), "Open Mesh Definition", UserProjectPath, "Mesh Definition File (*.meshdef);;" ).str();
			if( path.empty() ) return;
			TiXmlDocument doc( path.c_str() );
			if( !doc.LoadFile( ) )
				throw Lit::IOError( path, "Cannot load file because: "+std::string(doc.ErrorDesc()), EXC_LOC );
			TiXmlElement *pRoot = doc.RootElement();
			
			if( !pRoot || (std::string(pRoot->Value()) != "MeshDefinition") ) throw Lit::BadMeshDefinition( path, "MeshDefinition node not found or is not the Root node", EXC_LOC );
			
			std::string mesh = pRoot->Attribute("Mesh");
			if( mesh.empty() ) throw Lit::BadMeshDefinition( path, "No Mesh attribute specified for Root node", mesh, EXC_LOC );
			
			TiXmlElement *geoNode = pRoot->FirstChildElement("Geometry");
			TiXmlElement *matNode = pRoot->FirstChildElement("Material");
			TiXmlElement *phyNode = pRoot->FirstChildElement("Physics");

			if( !geoNode ) throw Lit::BadMeshDefinition( path, "Geometry node not found", mesh, EXC_LOC );
			if( !matNode ) throw Lit::BadMeshDefinition( path, "Material node not found", mesh, EXC_LOC );
			if( !phyNode ) throw Lit::BadMeshDefinition( path, "Physics node not found", mesh, EXC_LOC );

			
			std::string facdir = geoNode->Attribute("FacingDir"),
						matnam = matNode->Attribute("Name"),
						mass = phyNode->Attribute("Mass"),
						offset = phyNode->Attribute("Offset"),
						shape = phyNode->Attribute("Shape"),
						dyn = phyNode->Attribute("Dynamic");
			
			if( facdir.empty() ) throw Lit::BadMeshDefinition( path, "Geometry::FacingDir attribute not found", mesh, EXC_LOC );	
			if( matnam.empty() ) throw Lit::BadMeshDefinition( path, "Material::Name attribute not found", mesh, EXC_LOC );	
			if( mass.empty() ) throw Lit::BadMeshDefinition( path, "Physics::Mass attribute not found", mesh, EXC_LOC );	
			if( offset.empty() ) throw Lit::BadMeshDefinition( path, "Physics::Offset attribute not found", mesh, EXC_LOC );	
			if( shape.empty() ) throw Lit::BadMeshDefinition( path, "Physics::Shape attribute not found", mesh, EXC_LOC );	
			if( dyn.empty() ) throw Lit::BadMeshDefinition( path, "Physics::Dynamic attribute not found", mesh, EXC_LOC );	

			Physics::ShapeType shapet = Physics::ST_NULL;
			if( shape == Physics::trShapeType( Physics::ST_BOX ) ) shapet = Physics::ST_BOX;
			if( shape == Physics::trShapeType( Physics::ST_SPHERE ) ) shapet = Physics::ST_SPHERE;
			if( shape == Physics::trShapeType( Physics::ST_PLANE ) ) shapet = Physics::ST_PLANE;
			
			create( mesh, Ogre::StringConverter::parseBool( dyn ), shapet, Ogre::StringConverter::parseReal(mass), Ogre::StringConverter::parseVector3(offset) );
			
			mEntity->setMaterialName( matnam );
			
			if( facdir == "UNIT_X" ) mGeometryFacing->setCurrentIndex( 0 );
			else if( facdir == "UNIT_Y" ) mGeometryFacing->setCurrentIndex( 1 );
			else if( facdir == "UNIT_Z" ) mGeometryFacing->setCurrentIndex( 2 );
			else if( facdir == "NEGATIVE_UNIT_X" ) mGeometryFacing->setCurrentIndex( 3 );
			else if( facdir == "NEGATIVE_UNIT_Y" ) mGeometryFacing->setCurrentIndex( 4 );
			else if( facdir == "NEGATIVE_UNIT_Z" ) mGeometryFacing->setCurrentIndex( 5 );
			else throw Lit::BadMeshDefinition( path, "Invalid facing direction '"+facdir+"'", mesh, EXC_LOC );
		}
		void MeshProject::save( const std::string &filename )
		{
			std::string path = filename;
			if( filename.empty() )
			{
				path = FileDialog::getSaveFileName( getInstance(), "Save Mesh Definition", UserProjectPath, "Mesh Definition File (*.meshdef);;" ).str();
				if( !path.empty() )
					mSavePath = path;
			}
			if( path.empty() ) return;			
			TiXmlDocument doc(path.c_str());
			doc.LinkEndChild( new TiXmlDeclaration( PROJECT_VERSION_STRING, "UTF-8", "yes" ) );
			
			TiXmlElement * rootElem = new TiXmlElement( "MeshDefinition" );
			rootElem->SetAttribute( "Mesh", mEntity->getMeshName().c_str() );
			
			TiXmlElement * geoElem = new TiXmlElement( "Geometry" );
			TiXmlElement * matElem = new TiXmlElement( "Material" );
			TiXmlElement * phyElem = new TiXmlElement( "Physics" );
			
			geoElem->SetAttribute( "FacingDir", FROM_QT(mGeometryFacing->currentText()).c_str() );
			
			matElem->SetAttribute( "Name", FROM_QT(mMaterialName->currentText()).c_str() );
			
			phyElem->SetAttribute( "Shape", FROM_QT(mPhysicsShape->currentText()).c_str() );
			phyElem->SetAttribute( "Dynamic", Ogre::StringConverter::toString( mPhysicsDynamic->isChecked(), false ).c_str() );
			phyElem->SetAttribute( "Mass", Ogre::StringConverter::toString( Lit::Real(mPhysicsMass->value()) ).c_str() );
			phyElem->SetAttribute( "Offset", FROM_QT(mPhysicsOffset->text()).c_str() );

			rootElem->LinkEndChild( geoElem );
			rootElem->LinkEndChild( matElem );
			rootElem->LinkEndChild( phyElem );
			doc.LinkEndChild( rootElem );

			doc.SaveFile();
		}
		void MeshProject::importMesh( const std::string &filename )
		{
			try
			{
				FilePath path(filename.empty()?(FileDialog::getOpenFileName( mInstance, "Import Mesh", ImportPath, mMeshConverter.getFormatsAsString(true, false) ).str()):filename);
				if( path.str().empty() )
					return;
				Ogre::MeshPtr mesh( mMeshConverter.importMesh( path.str() ) );
				if( !mesh.isNull() )
				{
					if( mesh->isLoaded() ) mesh->load();
					if( isCreated() )
						create( mesh->getName(), true, Physics::ST_BOX, mPhysicsMass->value(), Ogre::StringConverter::parseVector3(FROM_QT(mPhysicsOffset->text())) );
					else create( mesh->getName() );
					MeshIconDatabase::Instance().createMeshSnapshot( mesh->getName() );
				}
			}
			catch( std::exception & e )
			{
				QMessageBox::warning( mTree, e.what(), e.what() );
			}
		}

		void MeshProject::exportMesh( const std::string &filename )
		{
			try
			{
				if( !mEntity ) return;
				FilePath path(filename.empty()?(FileDialog::getSaveFileName( mInstance, "Export Mesh", ExportPath, mMeshConverter.getFormatsAsString(false, true) ).str()):filename);
				if( path.str().empty() )
					return;
				mMeshConverter.exportMesh( path.str(), mEntity->getMesh() );
			}
			catch( std::exception & e )
			{
				QMessageBox::warning( mTree, e.what(), e.what() );
			}
		}

		void MeshProject::physicsMassChanged( double mass )
		{
			if( mBody ){
				if( !mBody->isStatic() )
				{
					mBody->setMassMatrix( Physics::MassMatrix( mass, mBody->getInertia() ) );
					LIT_LOG_DBG("Mass set to "+String(mass).str());
				}
			}
		}

		void MeshProject::physicsDynamicStateChanged( bool state )
		{
			if( mBody ){
				static Lit::Real lastMass = 0.0f;
				if( !state ) lastMass = mBody->getMass();
				mBody->setMassMatrix( Physics::MassMatrix( state ? lastMass : 0.0f, mBody->getInertia() ) );
				mBody->_setStatic( !state );
				mBody->setFreeState( !state );
				LIT_LOG_DBG("Physics Dynamic State changed to "+String(state).str());
			}
		}
		void MeshProject::materialChanged( const QString &to )
		{
			if( Ogre::MaterialManager::getSingleton().resourceExists( FROM_QT(to) ) )
				mEntity->setMaterialName( FROM_QT(to) );
		}
		void MeshProject::physicsShapeChanged( const QString &to )
		{
			LIT_LOG_DBG("Physics Shape changed to '"+FROM_QT(to)+"'");
		}
		void MeshProject::geometryFacingDirChanged( const QString &to )
		{
			LIT_LOG_DBG("Geometry Facing Direction Changed to '"+FROM_QT(to)+"'");
			if( !mFacingDir ) return;
			if( to == "UNIT_X" ) mFacingDir->setDirection( Lit::Vec3::UNIT_X );
			else if( to == "UNIT_Y" ) mFacingDir->setDirection( Lit::Vec3::UNIT_Y );
			else if( to == "UNIT_Z" ) mFacingDir->setDirection( Lit::Vec3::UNIT_Z );
			else if( to == "NEGATIVE_UNIT_X" ) mFacingDir->setDirection( Lit::Vec3::NEGATIVE_UNIT_X );
			else if( to == "NEGATIVE_UNIT_Y" ) mFacingDir->setDirection( Lit::Vec3::NEGATIVE_UNIT_Y );
			else if( to == "NEGATIVE_UNIT_Z" ) mFacingDir->setDirection( Lit::Vec3::NEGATIVE_UNIT_Z );
			else throw Lit::InvalidParameter( "FacingDir", "Unknown Axis Direction '"+FROM_QT(to)+"'", EXC_LOC );
		}
		
		const Physics::BodyPtr& MeshProject::getBody() const{ return mBody; }
		const Lit::Entity * MeshProject::getEntity() const{ if( mEntity ) return *mEntity; return NULL; }
		Lit::Entity* MeshProject::getEntity(){ if( mEntity ) return *mEntity; return NULL; }

		void MeshProject::fitEntity( )
		{
			if( !mEntity ) return;
			Ogre::Vector3 vSize = mEntity->getBoundingBox().getHalfSize();
			Ogre::Vector3 vCenter = mEntity->getBoundingBox().getCenter(); 
   
			vSize += Ogre::Vector3(vSize.z, vSize.z, vSize.z);

			float maxsize = std::max(std::max(vSize.x,vSize.y),vSize.z);
   
			vSize = Ogre::Vector3(0, 0, maxsize * 1.1f) + vCenter;
   
			Lit::Root::Instance().ogreCamera()->setPosition(vSize.x,vSize.y,vSize.z);
			Lit::Root::Instance().ogreCamera()->lookAt(vCenter.x,vCenter.y,vCenter.z);
		}
		void MeshProject::create( const std::string &mesh, bool dynamic, Lit::Physics::ShapeType shape, Lit::Real mass, const Lit::Vec3 &offset )
		{
			if( mesh.empty() ) return;
			Project::create( mesh );
			mEntity = Lit::Root::SceneManager()->createEntity( mesh, NULL, NULL, offset );
			Lit::AABB box(Ogre::MeshPtr(Ogre::MeshManager::getSingleton().getByName( mesh ))->getBounds());
			if( dynamic ) mBody = Lit::Root::SceneManager()->createDynamicBody( Lit::Root::PhysicsManager()->getWorld(), mEntity, mass, shape, 0, 0, Lit::Vec3( 0.0, box.getSize().y, 0.0f ) );
			else mBody = Lit::Root::SceneManager()->createStaticBody( Lit::Root::PhysicsManager()->getWorld(), mEntity, shape, 0, 0, Lit::Vec3( 0.0, box.getSize().y, 0.0f ) );
			mFacingDir = new MeshFacingDir( mEntity->getEntity()->getBoundingRadius() * 3.0f, *Lit::Root::SceneManager()->_sceneManager(), *mEntity->getNode() );
			fitEntity( );
			setupNodes();
		}
		void MeshProject::create( )
		{
			Lit::Editor::MeshSelecterDialog dlg( mTree, Lit::Root::Instance().getResourcesList() );
			std::string mesh = dlg.exec();
			if( mesh.empty() ) return;
			create( mesh, true, Physics::ST_BOX, 1.0f, Vec3::ZERO );
		}
	};
};